﻿#region [===== Using =====]
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
#endregion

namespace TPG.SharePoint.Workflow
{
    /// <summary>
    /// A base page for workflow bulk task editing forms.
    /// </summary>
    /// <remarks>
    /// <para>This class implements the generic framework code for handling 
    /// bulk task operations. The following tasks are performed by this class.</para>
    /// <list type="bullet">
    ///     <item>Retrieve all task list items for a given workflow association
    ///     and task type.</item>
    ///     <item>Updating task list items in bulk.</item>
    /// </list>
    /// <para>A <see cref="BulkTaskPage" /> is special ASPX page available for
    /// editing tasks in bulk. SharePoint does not provide this feature out of
    /// the box. As the implementer of bulk task forms you can derive from this 
    /// class to implement the generic aspects of the
    /// bulk task form. The non-generic data contained in the form is available as a
    /// <see cref="TaskItemCollection" /> object. Using the
    /// <see cref="BulkTaskPage.CommitTaskItems" /> method you commit the 
    /// tasks and provide custom data for each distinct task. The 
    /// <see cref="BulkTaskPage.CancelTaskItems" /> method redirects the 
    /// user back to the workflow settings page without updating any task. 
    /// Existing task data can be retrieved using the 
    /// <see cref="BulkTaskPage.GetTaskItems" /> method.</para>
    /// </remarks>
    public class BulkTaskPage 
        : LayoutWorkflowPage
    {
        #region [===== Fields =====]
        private SPListItemCollection _taskListItems;
        #endregion

        #region [===== Properties =====]
        /// <summary>
        /// Gets the <see cref="SPContentType" /> used by the tasks being 
        /// edited.
        /// </summary>
        /// <remarks>
        /// The framework uses the content type to provide advanced validation
        /// of data for the task.
        /// </remarks>
        protected virtual SPContentType TaskContentType
        {
            get { return Web.AvailableContentTypes[
                SPBuiltInContentTypeId.WorkflowTask]; }
        }

        /// <summary>
        /// Gets the unique identifier for the type of task being edited
        /// in the bulk task form.
        /// </summary>
        protected int TaskType
        {
            get { return QueryStringParser.GetParameter<int>("TaskType"); }
        }

        /// <summary>
        /// Gets the <see cref="SPList" /> which stores the tasks for the 
        /// workflow.
        /// </summary>
        protected SPList TaskList
        {
            get { return Web.Lists[WorkflowAssociationInternal.TaskListId]; }
        }

        /// <summary>
        /// Gets the <see cref="SPList" />.
        /// </summary>
        protected SPList List
        {
            get { return SPContext.Current.List; }
        }
        
        Guid WorkflowAssociationID
        {
            get { return QueryStringParser.GetParameter<Guid>("AssocID"); }
        }

        SPListItemCollection TaskListItemsInternal
        {
            get
            {
                if (_taskListItems == null)
                {
                    SPQuery query = new SPQuery();
                    query.Query =
                        String.Format(
                            CultureInfo.InvariantCulture,
                            @"<Where>
                                <And>
                                    <Eq>
                                        <FieldRef Name='ContentType' />
                                        <Value Type='Choice'>{0}</Value>
                                    </Eq>
                                    <And>
                                        <Eq>
                                            <FieldRef Name='Completed' />
                                            <Value Type='Integer'>0</Value>
                                        </Eq>
                                        <And>
                                            <Eq>
                                                <FieldRef Name='TaskType' />
                                                <Value Type='Integer'>{1}</Value>
                                            </Eq>
                                            <Eq>
                                                <FieldRef Name='WorkflowListId' />
                                                <Value Type='Guid'>{2}</Value>
                                            </Eq>
                                        </And>
                                    </And>
                                </And>
                            </Where>",
                            this.TaskContentType.Name, this.TaskType, this.List.ID);
                    _taskListItems = TaskList.GetItems(query);
                }
                return _taskListItems;
            }
        }
        #endregion

        #region [===== Internal methods =====]
        internal override SPWorkflowAssociation GetWorkflowAssociation()
        {
            return List.WorkflowAssociations[WorkflowAssociationID];
        }

        internal override SPWorkflowTemplate GetWorkflowTemplate()
        {
            return WorkflowAssociationInternal.BaseTemplate;
        }
        internal override WorkflowMetaData GetMetaData()
        {
            return new BulkTaskMetaData()
            {
                PageType = PageType.BulkTask,
                WorkflowName = WorkflowAssociationInternal.Name
            };
        }
        #endregion 

        #region [====== Protected methods =====]
        /// <summary>
        /// Gets the tasks that are available for editing in the form.
        /// </summary>
        /// <returns>A <see cref="TaskItemCollection" /> with the tasks that
        /// can be edited in the form.</returns>
        protected TaskItemCollection GetTaskItems()
        {
            Guid associationID = WorkflowAssociationID;
            TaskItemCollection results = new TaskItemCollection(TaskContentType);
            SPContentTypeId wss3wfTaskContentTypeID =
                new SPContentTypeId(ContentTypeID.WorkflowTask);
            bool allowExtendedProperties = 
                TaskContentType.Id == wss3wfTaskContentTypeID || 
                TaskContentType.Id.IsChildOf(wss3wfTaskContentTypeID);

            foreach (SPListItem item in TaskListItemsInternal)
            {
                Guid workflowId = new Guid(item[SPBuiltInFieldId.WorkflowInstanceID] as string);
                SPWorkflow workflowInstance = new SPWorkflow(this.Web, workflowId);

                if (workflowInstance.AssociationId == associationID)
                {
                    TaskItem taskItem = results.NewTaskItem(item.ID,
                        workflowInstance.ParentItem.DisplayName,
                        item, SPWorkflowTask.GetExtendedPropertiesAsHashtable(item));
                    taskItem.AllowExtendedProperties = allowExtendedProperties;
                }
            }
            return results;
        }

        /// <summary>
        /// Saves changes made to a set of tasks and redirects to the 
        /// workflow overview page.
        /// </summary>
        /// <param name="dataItems">The tasks to save.</param>
        protected void CommitTaskItems(TaskItemCollection dataItems)
        {
            using (SPLongOperation operation = new SPLongOperation(this))
            {
                operation.LeadingHTML = GetProgressHtml();
                operation.Begin();

                foreach (TaskItem data in dataItems)
                {
                    SPListItem listItem = TaskList.GetItemById(data.ListItemId);
                    SPWorkflowTask.AlterTask(
                        listItem, data.GetChanges(), true);
                }
                operation.End(this.List.DefaultViewUrl, SPRedirectFlags.UseSource, this.Context, null);
            }
        }

        /// <summary>
        /// Redirects back to the workflow overview page without saving
        /// any data.
        /// </summary>
        protected void CancelTaskItems()
        {
            SPUtility.Redirect(this.List.DefaultViewUrl,
                SPRedirectFlags.UseSource, Context);
        }

        /// <summary>
        /// Gets the HTML to display in the progress screen.
        /// </summary>
        /// <returns>A string of HTML, without the HTML, 
        /// BODY or FORM tags</returns>
        protected virtual string GetProgressHtml()
        {
            return null;
        }
        #endregion
    }
}
